Skip to content

Conversation

@singhkunal2050
Copy link
Collaborator

@singhkunal2050 singhkunal2050 commented Apr 24, 2025

Changes

  1. Supporting Custom Clevertap ID : https://wizrocket.atlassian.net/wiki/spaces/EN/pages/5546246181/Custom+CleverTap+ID+Support+for+Web+SDK
  2. Adding new public API called clevertap.getAllQualifiedCampaignDetails() for getting all qualified campaign details durign debugging

Changes to Public Facing API if any

  1. Details covered here for init changes here
  2. Added clevertap.getAllQualifiedCampaignDetails()

How Has This Been Tested?

  1. Added the build to the https://clevertap-sdk-test.netlify.app/?region=eu1&accountId=ZWW-WWW-WWRZ and verified
    1. New Custom GUID Flow - creates a new custom id
    2. No New id created when existing id is present
  2. Ran campaigns and used getAllQualifiedCampaignDetails to get their details during debugging in console

Checklist

  • Code compiles without errors
  • Version Bump added to package.json & CHANGELOG.md
  • All tests pass
  • Build process is successful
  • Documentation has been updated (if needed)

Summary by CodeRabbit

  • New Features

    • Added support for custom CleverTap IDs during initialization, with validation and sanitization.
    • Introduced tracking and storage of qualified campaign details for the current user, accessible for debugging or inspection.
    • Provided a method to retrieve all stored qualified campaign details.
  • Bug Fixes

    • Improved message handling to prevent unnecessary rendering when no message is provided.
  • Chores

    • Enhanced internal validation and storage logic for campaign and device identifiers.

@coderabbitai
Copy link

coderabbitai bot commented Apr 24, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@singhkunal2050 singhkunal2050 marked this pull request as ready for review April 28, 2025 05:05
@singhkunal2050 singhkunal2050 requested a review from a team as a code owner April 28, 2025 05:05
@singhkunal2050 singhkunal2050 marked this pull request as draft April 28, 2025 05:05
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (9)
src/modules/device.js (1)

12-12: Remove debugging console.log statement.

A console.log statement has been left in the code which should be removed before production release.

-    console.log('this.gcookie', this.gcookie)
src/util/tr.js (2)

31-31: Verify that import order is consistent with project standards.

The new import addCampaignToLocalStorage has been added to an existing import list. Make sure this follows the project's import ordering conventions.


38-41: Add a parameter comment to document the region parameter.

The _tr function signature has been updated to accept a region parameter, but it's not documented in the function's comments. Consider adding a comment to explain its purpose and expected type.

 const _tr = (msg, {
   device,
   session,
   request,
   logger,
+  // region: The account region for tracking campaigns
   region
 }) => {

Also applies to: 45-47

src/util/helpers.js (1)

138-153: LGTM! Good prefix implementation but consider making this public.

The addWebPrefix function properly enforces type checking and prefixes the ID with the platform-specific prefix. However, since this is a helper function that might be needed elsewhere, consider making it exported rather than private.

This could be exported as a named export if other parts of the SDK need to use this function directly.

-function addWebPrefix (id) {
+export function addWebPrefix (id) {
clevertap.js (5)

155-156: Consider future-proofing the qualified-campaigns cookie.

QUALIFIED_CAMPAIGNS is introduced as a dedicated cookie/local-storage key, which is great.
However, because this array is being appended to indefinitely (see addCampaignToLocalStorage), it can quickly exceed both cookie-size limits (~4 KB) and local-storage quotas (5–10 MB, but shared per-origin). A rolling window (e.g. last N campaigns or max age) will avoid silent write failures and corrupted values.


222-223: Prefix constant looks good – minor naming nit.

CUSTOM_CT_ID_PREFIX is clear and self-explanatory. As it is web-specific, consider WEB_CUSTOM_CT_ID_PREFIX to mirror Android/iOS constants and avoid cross-platform confusion.
Nothing blocking.


12624-12688: Validation helper is solid – two minor tweaks.

  1. allowedPattern need not allow upper-case because we already lowercase the input.
  2. The function returns {error} when invalid, yet callers log sanitizedId, leading to undefined messages (see next comment). Exporting the error makes troubleshooting easier.
-    const allowedPattern = /^[a-z0-9()!:@$_-]+$/;
+    const allowedPattern = /^[a-z0-9()!:@$_-]+$/; // lower-case only

and ensure consumers read error.


17018-17020: Invocation order good, but propagate validation errors.

createCustomIdIfValid is called after updating account context – good. Consider bubbling up the error return to callers (or throwing) so integrators can surface invalid IDs in their own logs/UI instead of the SDK silently swallowing them.


17246-17249: Return a safe default rather than undefined.

getAllQualifiedCampaignDetails yields undefined when nothing is stored.
Returning [] makes client-side handling cleaner:

-      const existingCampaign = StorageManager.readFromLSorCookie(QUALIFIED_CAMPAIGNS) && JSON.parse(...);
-      return existingCampaign;
+      const stored = StorageManager.readFromLSorCookie(QUALIFIED_CAMPAIGNS);
+      return stored ? JSON.parse(decodeURIComponent(stored)) : [];
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ee2510 and cefe652.

⛔ Files ignored due to path filters (2)
  • clevertap.js.map is excluded by !**/*.map
  • clevertap.min.js is excluded by !**/*.min.js
📒 Files selected for processing (8)
  • clevertap.js (15 hunks)
  • src/clevertap.js (7 hunks)
  • src/modules/device.js (1 hunks)
  • src/modules/web-inbox/Message.js (1 hunks)
  • src/util/campaignRender/utilities.js (2 hunks)
  • src/util/constants.js (2 hunks)
  • src/util/helpers.js (2 hunks)
  • src/util/tr.js (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/util/constants.js (1)
clevertap.js (2)
  • QUALIFIED_CAMPAIGNS (155-155)
  • CUSTOM_CT_ID_PREFIX (222-222)
src/util/helpers.js (1)
src/util/constants.js (2)
  • CUSTOM_CT_ID_PREFIX (72-72)
  • CUSTOM_CT_ID_PREFIX (72-72)
src/util/campaignRender/utilities.js (2)
src/util/storage.js (1)
  • StorageManager (9-246)
src/util/constants.js (2)
  • QUALIFIED_CAMPAIGNS (9-9)
  • QUALIFIED_CAMPAIGNS (9-9)
🪛 GitHub Actions: Test and build
src/clevertap.js

[error] 108-108: TypeError: Cannot read properties of undefined (reading 'isValid') in constructor and multiple init tests. This error occurs repeatedly at line 108 in src/clevertap.js during test/unit/clevertap.spec.js tests.

clevertap.js

[error] 108-108: TypeError: Cannot read properties of undefined (reading 'isValid') in constructor and multiple init tests. This error occurs repeatedly at line 108 in src/clevertap.js during test/unit/clevertap.spec.js tests.

🔇 Additional comments (18)
src/util/constants.js (2)

9-9: New constant added for qualified campaigns storage.

The QUALIFIED_CAMPAIGNS constant defines the key for storing qualified campaign data in local storage. This aligns with the PR objective to implement a new public API method for retrieving qualified campaign details during debugging.


72-72: New constant added for custom CleverTap ID prefix.

The CUSTOM_CT_ID_PREFIX constant defines the platform-specific prefix ('w') for custom CleverTap IDs. This supports the PR objective to add custom CleverTap ID functionality.

src/util/campaignRender/utilities.js (2)

13-14: Added required imports for new functionality.

Importing the necessary constants to support the new campaign storage functionality and visual editor types.


356-377: Well-implemented campaign storage functionality.

This function enriches campaign objects with dashboard URLs and persists them to local storage, with proper duplicate prevention. It successfully implements part of the PR objective to support retrieving qualified campaign details for debugging.

The implementation correctly:

  • Extracts the campaign ID from the wzrk_id
  • Constructs a dashboard URL using region and accountId
  • Prevents duplicate entries by checking existing campaigns
  • Uses proper encoding/decoding for storage
src/modules/web-inbox/Message.js (1)

8-8: Added null check for renderMessage call.

This defensive programming change ensures the renderMessage method is only called when a message is actually provided, preventing potential errors when the message parameter is null or undefined.

src/modules/device.js (2)

9-9: Updated constructor to accept customId parameter.

Constructor now accepts an object with both logger and customId properties, supporting the custom CleverTap ID feature from the PR objectives.


11-11: Added fallback logic for custom CleverTap ID.

This line correctly implements the fallback mechanism for custom IDs, using the existing GUID if available and falling back to the custom ID if not.

src/util/tr.js (2)

849-850: LGTM! Campaign tracking implementation.

The implementation correctly passes the campaign, region, and account ID to addCampaignToLocalStorage for in-app notifications.


935-936: LGTM! Campaign tracking for inbox notifications.

The implementation correctly passes inbox notifications to addCampaignToLocalStorage, consistent with the in-app notification implementation.

src/util/helpers.js (2)

1-2: LGTM! Good import practice for constants.

Importing the constant from the constants file follows good practices for maintainability.


99-136: LGTM! Well-documented and thorough validation function.

The validateCustomCleverTapID function is well-documented with clear comments about the validation rules. It properly handles validation for:

  • Input type (must be a string)
  • Length constraints (1-64 characters)
  • Allowed characters (using a regex pattern)

The function returns an object with validation results, error messages, and a sanitized ID, which is a good pattern.

src/clevertap.js (6)

36-38: LGTM! Good import practice for constants.

Adding imports for the new constants needed for this feature.


658-673: LGTM! Well-implemented custom ID creation logic.

The createCustomIdIfValid method provides a clean way to validate and set custom IDs. It correctly:

  1. Validates the custom ID
  2. Checks if a device GUID already exists before applying the custom ID
  3. Updates both the in-memory representation and local storage
  4. Provides appropriate logging

675-677: LGTM! Good implementation of config handling in init.

The modified init method now properly accepts and processes the optional config parameter with antiFlicker and customId properties.


712-713: LGTM! Call to create custom ID in init method.

Calling createCustomIdIfValid in the init method ensures that custom IDs can be set during initialization.


593-595: LGTM! Added region to tr function call.

Properly forwarding the account region to the _tr function which enables region-aware campaign tracking.


1034-1041: LGTM! Well-implemented campaign details retrieval method.

The getAllQualifiedCampaignDetails method is well-documented and provides a clean way to access campaign details for debugging purposes. It correctly reads from local storage, parses the JSON, and returns the decoded campaign information.

clevertap.js (1)

10071-10075: Constructor guard improves stability – LGTM.
Rendering only when a message exists prevents a handful of null-pointer paths observed in earlier builds.

src/clevertap.js Outdated
Comment on lines 662 to 665
if (!this.#device.gcookie) {
return
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Logic error in GUID check - inverted condition

The current condition exits the function if the GUID exists, but the comment indicates that we should only add a custom ID if no existing ID is present. The condition appears to be inverted.

Correct the condition to match the intended behavior:

  /* Only add Custom Id if no existing id is present */
-  if (!this.#device.gcookie) {
+  if (this.#device.gcookie) {
     return
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!this.#device.gcookie) {
return
}
/* Only add Custom Id if no existing id is present */
if (this.#device.gcookie) {
return
}

Comment on lines 107 to 114

const { isValid, sanitizedId } = validateCustomCleverTapID(clevertap.config?.customId)
if (!isValid) {
this.#logger.error(sanitizedId)
}

this.#device = new DeviceManager({ logger: this.#logger, customId: isValid ? sanitizedId : null })
this.#dismissSpamControl = clevertap.dismissSpamControl || false
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential undefined error when accessing config properties.

There's a pipeline failure here where the code attempts to access properties of potentially undefined objects. The optional chaining on clevertap.config?.customId is correct, but it appears validateCustomCleverTapID might be receiving undefined and trying to destructure properties from the result.

Apply this fix to handle cases where the config is not provided:

-    const { isValid, sanitizedId } = validateCustomCleverTapID(clevertap.config?.customId)
-    if (!isValid) {
-      this.#logger.error(sanitizedId)
+    let isValid = false;
+    let sanitizedId = null;
+    
+    if (clevertap.config?.customId) {
+      const result = validateCustomCleverTapID(clevertap.config.customId);
+      isValid = result.isValid;
+      if (!isValid) {
+        this.#logger.error(result.error);
+      } else {
+        sanitizedId = result.sanitizedId;
+      }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { isValid, sanitizedId } = validateCustomCleverTapID(clevertap.config?.customId)
if (!isValid) {
this.#logger.error(sanitizedId)
}
this.#device = new DeviceManager({ logger: this.#logger, customId: isValid ? sanitizedId : null })
this.#dismissSpamControl = clevertap.dismissSpamControl || false
// Guard against missing config.customId
let isValid = false;
let sanitizedId = null;
if (clevertap.config?.customId) {
const result = validateCustomCleverTapID(clevertap.config.customId);
isValid = result.isValid;
if (!isValid) {
this.#logger.error(result.error);
} else {
sanitizedId = result.sanitizedId;
}
}
this.#device = new DeviceManager({
logger: this.#logger,
customId: isValid ? sanitizedId : null
});
this.#dismissSpamControl = clevertap.dismissSpamControl || false;
🧰 Tools
🪛 GitHub Actions: Test and build

[error] 108-108: TypeError: Cannot read properties of undefined (reading 'isValid') in constructor and multiple init tests. This error occurs repeatedly at line 108 in src/clevertap.js during test/unit/clevertap.spec.js tests.

Comment on lines 16288 to 16304
_classPrivateFieldLooseBase(this, _logger)[_logger] = new Logger(logLevels.INFO);
_classPrivateFieldLooseBase(this, _account)[_account] = new Account((_clevertap$account = clevertap.account) === null || _clevertap$account === void 0 ? void 0 : _clevertap$account[0], clevertap.region || ((_clevertap$account2 = clevertap.account) === null || _clevertap$account2 === void 0 ? void 0 : _clevertap$account2[1]), clevertap.targetDomain || ((_clevertap$account3 = clevertap.account) === null || _clevertap$account3 === void 0 ? void 0 : _clevertap$account3[2]), clevertap.token || ((_clevertap$account4 = clevertap.account) === null || _clevertap$account4 === void 0 ? void 0 : _clevertap$account4[3]));
encryption.key = (_clevertap$account5 = clevertap.account) === null || _clevertap$account5 === void 0 ? void 0 : _clevertap$account5[0].id;
encryption.key = (_clevertap$account5 = clevertap.account) === null || _clevertap$account5 === void 0 ? void 0 : _clevertap$account5[0].id; // Custom Guid will be set here

const {
isValid,
sanitizedId
} = validateCustomCleverTapID((_clevertap$config = clevertap.config) === null || _clevertap$config === void 0 ? void 0 : _clevertap$config.customId);

if (!isValid) {
_classPrivateFieldLooseBase(this, _logger)[_logger].error(sanitizedId);
}

_classPrivateFieldLooseBase(this, _device)[_device] = new DeviceManager({
logger: _classPrivateFieldLooseBase(this, _logger)[_logger]
logger: _classPrivateFieldLooseBase(this, _logger)[_logger],
customId: isValid ? sanitizedId : null
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Incorrect error logging & potential source of the unit-test failure.

When the supplied customId is invalid, the destructuring yields sanitizedId = undefined, so

_logger.error(sanitizedId); // logs "undefined"

Meanwhile later code assumes isValid exists, which it will, but any earlier refactor that returns undefined would reproduce the pipeline error. Prefer logging the error string returned by the validator:

-      const { isValid, sanitizedId } = validateCustomCleverTapID(...);
+      const { isValid, sanitizedId, error } = validateCustomCleverTapID(...);

       if (!isValid) {
-        _classPrivateFieldLooseBase(this, _logger)[_logger].error(sanitizedId);
+        _classPrivateFieldLooseBase(this, _logger)[_logger].error(error);
       }

Comment on lines 7768 to 7780
let {
logger
logger,
customId
} = _ref;
Object.defineProperty(this, _logger$9, {
writable: true,
value: void 0
});
this.gcookie = void 0;
_classPrivateFieldLooseBase(this, _logger$9)[_logger$9] = logger;
this.gcookie = this.getGuid();
this.gcookie = this.getGuid() || customId;
console.log('this.gcookie', this.gcookie);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid unconditional console logging & clarify precedence of IDs.

  1. console.log('this.gcookie', this.gcookie); will leak into production bundles. Use the injected logger so the SDK honours the consumer’s log-level.

  2. this.gcookie = this.getGuid() || customId; gives priority to the stored GUID and uses the custom ID only when none exists – correct according to the PR description.
    ‑ If the intention is the inverse (always honour the supplied custom ID), swap the operands.

-      this.gcookie = this.getGuid() || customId;
-      console.log('this.gcookie', this.gcookie);
+      // prefer existing guid; fall back to validated customId
+      this.gcookie = this.getGuid() || customId;
+      _classPrivateFieldLooseBase(this, _logger$9)[_logger$9].debug('Device GUID set to', this.gcookie);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let {
logger
logger,
customId
} = _ref;
Object.defineProperty(this, _logger$9, {
writable: true,
value: void 0
});
this.gcookie = void 0;
_classPrivateFieldLooseBase(this, _logger$9)[_logger$9] = logger;
this.gcookie = this.getGuid();
this.gcookie = this.getGuid() || customId;
console.log('this.gcookie', this.gcookie);
}
let {
logger,
customId
} = _ref;
Object.defineProperty(this, _logger$9, {
writable: true,
value: void 0
});
this.gcookie = void 0;
_classPrivateFieldLooseBase(this, _logger$9)[_logger$9] = logger;
// prefer existing guid; fall back to validated customId
this.gcookie = this.getGuid() || customId;
_classPrivateFieldLooseBase(this, _logger$9)[_logger$9].debug('Device GUID set to', this.gcookie);
}

Comment on lines 16947 to 16966
createCustomIdIfValid(customId) {
const {
isValid,
sanitizedId
} = validateCustomCleverTapID(customId);
/* Only add Custom Id if no existing id is present */

if (!_classPrivateFieldLooseBase(this, _device)[_device].gcookie) {
return;
}

if (isValid) {
_classPrivateFieldLooseBase(this, _device)[_device].gcookie = sanitizedId;
StorageManager.saveToLSorCookie(GCOOKIE_NAME, sanitizedId);

_classPrivateFieldLooseBase(this, _logger)[_logger].debug('CT Initialized with customId:: ' + sanitizedId);
} else {
_classPrivateFieldLooseBase(this, _logger)[_logger].error('Invalid customId');
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Logic inverted – custom ID never persisted for first-time users.

The guard clause returns when no gcookie exists, contrary to the comment:

-      /* Only add Custom Id if no existing id is present */
-      if (!_classPrivateFieldLooseBase(this, _device)[_device].gcookie) {
-        return;
-      }
+      /* Only add Custom Id if no existing id is present */
+      if (_classPrivateFieldLooseBase(this, _device)[_device].gcookie) {
+        return; // GUID already present – leave untouched
+      }

This bug prevents the custom ID from being stored on fresh installs and matches the test failures reporting isValid as undefined.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
createCustomIdIfValid(customId) {
const {
isValid,
sanitizedId
} = validateCustomCleverTapID(customId);
/* Only add Custom Id if no existing id is present */
if (!_classPrivateFieldLooseBase(this, _device)[_device].gcookie) {
return;
}
if (isValid) {
_classPrivateFieldLooseBase(this, _device)[_device].gcookie = sanitizedId;
StorageManager.saveToLSorCookie(GCOOKIE_NAME, sanitizedId);
_classPrivateFieldLooseBase(this, _logger)[_logger].debug('CT Initialized with customId:: ' + sanitizedId);
} else {
_classPrivateFieldLooseBase(this, _logger)[_logger].error('Invalid customId');
}
}
createCustomIdIfValid(customId) {
const {
isValid,
sanitizedId
} = validateCustomCleverTapID(customId);
/* Only add Custom Id if no existing id is present */
if (_classPrivateFieldLooseBase(this, _device)[_device].gcookie) {
return; // GUID already present – leave untouched
}
if (isValid) {
_classPrivateFieldLooseBase(this, _device)[_device].gcookie = sanitizedId;
StorageManager.saveToLSorCookie(GCOOKIE_NAME, sanitizedId);
_classPrivateFieldLooseBase(this, _logger)[_logger].debug('CT Initialized with customId:: ' + sanitizedId);
} else {
_classPrivateFieldLooseBase(this, _logger)[_logger].error('Invalid customId');
}
}

Comment on lines +11562 to +11578
function addCampaignToLocalStorage(campaign) {
let region = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'eu1';
let accountId = arguments.length > 2 ? arguments[2] : undefined;
const campaignId = campaign.wzrk_id.split('_')[0];
const dashboardUrl = "https://".concat(region, ".dashboard.clevertap.com/").concat(accountId, "/campaigns/campaign/").concat(campaignId, "/report/stats");
const enrichedCampaign = { ...campaign,
url: dashboardUrl
};
const storedData = StorageManager.readFromLSorCookie(QUALIFIED_CAMPAIGNS);
const existingCampaigns = storedData ? JSON.parse(decodeURIComponent(storedData)) : [];
const isDuplicate = existingCampaigns.some(c => c.wzrk_id === campaign.wzrk_id);

if (!isDuplicate) {
const updatedCampaigns = [...existingCampaigns, enrichedCampaign];
StorageManager.saveToLSorCookie(QUALIFIED_CAMPAIGNS, encodeURIComponent(JSON.stringify(updatedCampaigns)));
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Harden local-storage deserialisation & enforce a retention policy.

decodeURIComponent and JSON.parse will throw if the stored value is malformed (e.g. user manually clears part of the cookie). A defensive wrapper avoids cascading failures:

-    const storedData = StorageManager.readFromLSorCookie(QUALIFIED_CAMPAIGNS);
-    const existingCampaigns = storedData ? JSON.parse(decodeURIComponent(storedData)) : [];
+    let existingCampaigns = [];
+    try {
+      const storedData = StorageManager.readFromLSorCookie(QUALIFIED_CAMPAIGNS);
+      existingCampaigns = storedData ? JSON.parse(decodeURIComponent(storedData)) : [];
+    } catch (e) {
+      existingCampaigns = [];
+      logger$1?.warn('Corrupted QUALIFIED_CAMPAIGNS payload – resetting.', e);
+    }

Optional: cap the array length (e.g. 50) before saving to respect storage limits (see previous comment).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function addCampaignToLocalStorage(campaign) {
let region = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'eu1';
let accountId = arguments.length > 2 ? arguments[2] : undefined;
const campaignId = campaign.wzrk_id.split('_')[0];
const dashboardUrl = "https://".concat(region, ".dashboard.clevertap.com/").concat(accountId, "/campaigns/campaign/").concat(campaignId, "/report/stats");
const enrichedCampaign = { ...campaign,
url: dashboardUrl
};
const storedData = StorageManager.readFromLSorCookie(QUALIFIED_CAMPAIGNS);
const existingCampaigns = storedData ? JSON.parse(decodeURIComponent(storedData)) : [];
const isDuplicate = existingCampaigns.some(c => c.wzrk_id === campaign.wzrk_id);
if (!isDuplicate) {
const updatedCampaigns = [...existingCampaigns, enrichedCampaign];
StorageManager.saveToLSorCookie(QUALIFIED_CAMPAIGNS, encodeURIComponent(JSON.stringify(updatedCampaigns)));
}
}
function addCampaignToLocalStorage(campaign) {
let region = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'eu1';
let accountId = arguments.length > 2 ? arguments[2] : undefined;
const campaignId = campaign.wzrk_id.split('_')[0];
const dashboardUrl = "https://".concat(region, ".dashboard.clevertap.com/").concat(accountId, "/campaigns/campaign/").concat(campaignId, "/report/stats");
const enrichedCampaign = { ...campaign,
url: dashboardUrl
};
- const storedData = StorageManager.readFromLSorCookie(QUALIFIED_CAMPAIGNS);
- const existingCampaigns = storedData ? JSON.parse(decodeURIComponent(storedData)) : [];
+ let existingCampaigns = [];
+ try {
+ const storedData = StorageManager.readFromLSorCookie(QUALIFIED_CAMPAIGNS);
+ existingCampaigns = storedData ? JSON.parse(decodeURIComponent(storedData)) : [];
+ } catch (e) {
+ existingCampaigns = [];
+ logger$1?.warn('Corrupted QUALIFIED_CAMPAIGNS payload – resetting.', e);
+ }
const isDuplicate = existingCampaigns.some(c => c.wzrk_id === campaign.wzrk_id);
if (!isDuplicate) {
const updatedCampaigns = [...existingCampaigns, enrichedCampaign];
StorageManager.saveToLSorCookie(QUALIFIED_CAMPAIGNS, encodeURIComponent(JSON.stringify(updatedCampaigns)));
}
}

@singhkunal2050 singhkunal2050 changed the base branch from master to develop April 28, 2025 07:32
@singhkunal2050 singhkunal2050 changed the base branch from develop to master April 28, 2025 08:33
@singhkunal2050 singhkunal2050 changed the base branch from master to develop April 28, 2025 08:34
@singhkunal2050 singhkunal2050 changed the base branch from develop to master April 28, 2025 08:34
@singhkunal2050 singhkunal2050 changed the base branch from master to develop April 28, 2025 08:34
@singhkunal2050 singhkunal2050 marked this pull request as ready for review April 28, 2025 08:48
}
}

init (accountId, region, targetDomain, token, config = { antiFlicker: {}, customId: null }) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not change any existing parameters. This would be a breaking change

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have checked with the PM on this in the past, the antiflicker param is not being used by any customer and hence its format can be changed.

Refer : #368 (comment)

image

@singhkunal2050 singhkunal2050 merged commit 747a2cf into develop May 6, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants